import java.util.Random;

import javax.swing.JTextArea;


public abstract class Player {
	
	public static final int HIT = 1;
	public static final int EMPTY = 2;
	public static final int WON = 3;
	public static final int SUNK = 4;
	
	public static final int DELAY = 2000;
	
	
	public int ask(int x, int y) {
		int result;
		if (myBoard.getField(x, y) == Board.SHIP) {
			result = Player.HIT;
			myBoard.setField(x, y, Board.STROKE);
			if (isMySunk(x, y)) {
				setMySunk(x, y);
				result = Player.SUNK;
			}
		} else {
			result = Player.EMPTY;
			myBoard.setField(x, y, Board.TRIED);
		}
		return result;
	}
	
	public abstract int strike();
	
	public void setOpponent(Player opponent) {
		this.opponent = opponent;
	}
	
	
	protected Board myBoard; //my board
	protected Board opBoard; //opponnents board
	protected Random r;
	protected Player opponent;
	protected int gameSize = 0;
	protected String name;
	protected int allShipsFields = 0;
	protected int[] ships = new int[4];
	protected boolean playerWon = false;
	protected JTextArea textArea = null;
	
	public static final int[] SHIPS = {4, 3, 3, 2, 2, 2, 1, 1, 1, 1};
	
	public Player(int size, String name, JTextArea textArea) {
		this.gameSize = size;
		this.name = name;
		this.textArea = textArea;
		myBoard = new Board(gameSize + 2);
		opBoard = new Board(gameSize + 2);
		for (int i = 0; i < SHIPS.length; i++) {
			allShipsFields += SHIPS[i];
			ships[SHIPS[i] - 1]++; 
		}
		//initialize random generator with a seed of current time
		//r = new Random(System.currentTimeMillis());
		r = new Random();
	}
	
	public void putShipsRandomly() {
		try {
			Thread.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//for every ship
		for (int i = 0; i < SHIPS.length; i++) {
			if (!putShipRandomly(SHIPS[i])) {
				//failed to find place for ship
				//clear the board and restart search
				myBoard = new Board(gameSize + 2);
				putShipsRandomly();
				return;
			}
		}
		return;
	}
	
	protected boolean putShipRandomly(int size) {
		int startX = 1;
		int startY = 1;
		int direction = 0;
		
		//tries 1000 times and then fails
		//restarts the search
		int failsCounter = 1000;
		boolean succeeded = false;
		while (!succeeded) {
			//draw new start field
			startX = r.nextInt(gameSize) + 1;
			startY = r.nextInt(gameSize) + 1;
			direction = r.nextInt(2);
			if (shipFitsAndDoesntStick(size, startX, startY, direction)) {
				putShip(size, startX, startY, direction);
				succeeded = true;
			}
			if (--failsCounter < 0)
				return false;
		}
		return true;
	}

	/*
	 * direction 0 - along X axis
	 * direction 1 - along Y axis
	 */
	protected void putShip(int size, int startX, int startY, int direction) {
		//put ship horizontally or vertically
		int dx = 0;
		int dy = 0;
		if (direction == 0) {
			//horizontally
			dx = 1;
			dy = 0;
		} else {
			//vertically
			dx = 0;
			dy = 1;
		}
		int deltaX = 0;
		int deltaY = 0;
		for (int i = 0; i < size; i++) {
			myBoard.setField(startX + deltaX, startY + deltaY, Board.SHIP); 
			deltaX += dx;
			deltaY += dy;
		}		
	}
	
	protected boolean isMyShip(int x, int y) {
		if (myBoard.getField(x, y) == Board.SHIP)
			return true;
		return false;
	}
	
	protected boolean isMyStroke(int x, int y) {
		if (myBoard.getField(x, y) == Board.STROKE)
			return true;
		return false;
	}
	
	protected boolean isOpStroke(int x, int y) {
		if (opBoard.getField(x, y) == Board.STROKE)
			return true;
		return false;
	}
	
	protected boolean isMySinking(int x, int y) {
		if (opBoard.getField(x, y) == Board.SUNK)
			return true;
		return false;		
	}
		
	protected boolean isMySunk(int x, int y) {
		int orgX = x;
		int orgY = y;

		x = orgX;
		y = orgY;
		//N
		while (isMyStroke(x, y)) {
			y--;
		}
		if (isMyShip(x, y))
			return false;
		
		x = orgX;
		y = orgY;
		//E
		while (isMyStroke(x, y)) {
			x++;
		}
		if (isMyShip(x, y))
			return false;
		
		x = orgX;
		y = orgY;
		//S
		while (isMyStroke(x, y)) {
			y++;
		}
		if (isMyShip(x, y))
			return false;
		
		x = orgX;
		y = orgY;
		//W
		while (isMyStroke(x, y)) {
			x--;
		}
		if (isMyShip(x, y))
			return false;
		
		return true;
	}

	protected void setOpSunk(int x, int y) {
		int orgX = x;
		int orgY = y;

		x = orgX;
		y = orgY;
		//N
		while (isOpStroke(x, y)) {
			opBoard.setField(x, y, Board.SUNK);
			y--;
		}
		
		x = orgX + 1;
		y = orgY;
		//E
		while (isOpStroke(x, y)) {
			opBoard.setField(x, y, Board.SUNK);
			x++;
		}
		
		x = orgX;
		y = orgY + 1;
		//S
		while (isOpStroke(x, y)) {
			opBoard.setField(x, y, Board.SUNK);
			y++;
		}
		
		x = orgX - 1;
		y = orgY;
		//W
		while (isOpStroke(x, y)) {
			opBoard.setField(x, y, Board.SUNK);
			x--;
		}
	}
	
	protected void setMySunk(int x, int y) {
		int orgX = x;
		int orgY = y;

		x = orgX;
		y = orgY;
		//N
		while (isMyStroke(x, y)) {
			myBoard.setField(x, y, Board.SUNK);
			y--;
		}
		
		x = orgX + 1;
		y = orgY;
		//E
		while (isMyStroke(x, y)) {
			myBoard.setField(x, y, Board.SUNK);
			x++;
		}
		
		x = orgX;
		y = orgY + 1;
		//S
		while (isMyStroke(x, y)) {
			myBoard.setField(x, y, Board.SUNK);
			y++;
		}
		
		x = orgX - 1;
		y = orgY;
		//W
		while (isMyStroke(x, y)) {
			myBoard.setField(x, y, Board.SUNK);
			x--;
		}
	}
	
	/*
	 * direction 0 - along X axis
	 * direction 1 - along Y axis
	 */
	protected boolean shipFits(int size, int startX, int startY, int direction) {
		//put ship horizontally or vertically
		int dx = 0;
		int dy = 0;
		if (direction == 0) {
			//horizontally
			dx = 1;
			dy = 0;
		} else {
			//vertically
			dx = 0;
			dy = 1;
		}
		
		boolean fits = true;
		int deltaX = 0;
		int deltaY = 0;
		for (int i = 0; i < size; i++) {
			//check whether not going out of board bounds
			if (startX + deltaX > gameSize || startY + deltaY > gameSize)
				return false;
			fits = fits & myBoard.getField(startX + deltaX, startY + deltaY) == Board.FREE; 
			deltaX += dx;
			deltaY += dy;
		}
		return fits;
	}
	
	/*
	 * direction 0 - along X axis
	 * direction 1 - along Y axis
	 */
	protected boolean shipFitsAndDoesntStick(int size, int startX, int startY, int direction) {
		return freeNeighborhood(size, startX, startY, direction);
	}
	
	protected boolean freeNeighborhood(int size, int startX, int startY, int direction) {
		
		int newStartX = startX;
		int newStartY = startY;
		//checkNeighborhood
		// for example, for ship of size 4 we check
		//  xxxxxx
		//  xoooox
		//  xxxxxx
		// so we define a rectangle with starting point 
		// (startRectX, startRectY)
		// and ending point
		// (endRectX, endRectY)
		int startRectX = 0;
		int startRectY = 0;
		int endRectX = 0;
		int endRectY = 0;
		if (direction == 0) {
			startRectX = newStartX - 1;
			startRectY = newStartY - 1;
			endRectX = newStartX + size;
			endRectY = newStartY + 2;
		} else {
			startRectX = newStartX - 1;
			startRectY = newStartY - 1;
			endRectX = newStartX + 2;
			endRectY = newStartY + size;
		}	
		//ships exceed bounds of board
		if (endRectX >= gameSize + 2 || endRectY >= gameSize + 2)
			return false;
		
		//check this rectangle
		boolean free = true;
		for (int i = startRectX; i <= endRectX; i++) {
			for (int j = startRectY; j <= endRectY; j++) {
				free = free & (myBoard.getField(i, j) == Board.FREE || myBoard.getField(i, j) == Board.TRIED);
			}
		}
		
		return free;
	}
	
	public Board getMyBoard() {
		return myBoard;
	}
	
	public Board getOpBoard() {
		return opBoard;
	}
	
	
	public String toString() {
		return name;
	}
}
